package com.yun.zf.system.config;

import com.yun.zf.system.security.filter.JwtAuthenticationTokenFilter;
import com.yun.zf.system.security.filter.MobileCodeAuthenticationFilter;
import com.yun.zf.system.security.filter.WxAppletAuthenticationFilter;
import com.yun.zf.system.security.handle.*;
import com.yun.zf.system.security.provider.MobileCodeAuthenticationProvider;
import com.yun.zf.system.security.provider.WxAppletAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsUtils;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


        /**
         * 自定义用户认证逻辑
         */
        @Autowired
        private UserDetailsService userDetailsService;
        @Autowired
        private WxAppletAuthenticationProvider wxAppletAuthenticationProvider;
        @Autowired
        private MobileCodeAuthenticationProvider mobileCodeAuthenticationProvider;

        /**
         * 认证失败处理类
         */
        @Autowired
        private AuthenticationEntryPointImpl unauthorizedHandler;

        /**
         * 退出处理类
         */
        @Autowired
        private LogoutSuccessHandlerImpl logoutSuccessHandler;

        /**
         * token认证过滤器
         */
        @Autowired
        private JwtAuthenticationTokenFilter authenticationTokenFilter;

        @Autowired
        AjaxAccessDeniedHandler accessDeniedHandler;
        @Autowired
        private WxAppletAuthenticationnSuccessHandler wxAppletAuthenticationnSuccessHandler;
        @Autowired
        private WxAuthenticationFailureHandler wxAuthenticationFailureHandler;

        @Autowired
        private PasswordAuthenticationnSuccessHandler passwordAuthenticationnSuccessHandler;



        /**
         * anyRequest          |   匹配所有请求路径
         * access              |   SpringEl表达式结果为true时可以访问
         * anonymous           |   匿名可以访问
         * denyAll             |   用户不能访问
         * fullyAuthenticated  |   用户完全认证可以访问（非remember-me下自动登录）
         * hasAnyAuthority     |   如果有参数，参数表示权限，则其中任何一个权限可以访问
         * hasAnyRole          |   如果有参数，参数表示角色，则其中任何一个角色可以访问
         * hasAuthority        |   如果有参数，参数表示权限，则其权限可以访问
         * hasIpAddress        |   如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问
         * hasRole             |   如果有参数，参数表示角色，则其角色可以访问
         * permitAll           |   用户可以任意访问
         * rememberMe          |   允许通过remember-me登录的用户访问
         * authenticated       |   用户登录后可访问
         */
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception
        {
                httpSecurity
                        // CRSF禁用，因为不使用session
                        .csrf().disable()
                        // 基于token，所以不需要session
                        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                        // 过滤请求
                        .authorizeRequests()
                        .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                        // 对于登录login 验证码captchaImage 允许匿名访问
                        .antMatchers("/login", "/captchaImage","/wechatLogin","/userLogin").anonymous()
                        .antMatchers(
                                HttpMethod.GET,
                                "/*.html",
                                "/**/*.html",
                                "/**/*.css",
                                "/**/*.js"
                        ).permitAll()
                        .antMatchers("/camunda/**").anonymous()
                        .antMatchers("/profile/**").anonymous()
                        .antMatchers("/common/download**").anonymous()
                        .antMatchers("/common/download/resource**").anonymous()
                        .antMatchers("/swagger-ui.html").anonymous()
                        .antMatchers("/swagger-resources/**").anonymous()
                        .antMatchers("/webjars/**").anonymous()
                        .antMatchers("/*/api-docs").anonymous()
                        .antMatchers("/druid/**").anonymous()
                        .antMatchers("/home/**").anonymous()
                        .antMatchers("/search/**").anonymous()
                        // 除上面外的所有请求全部需要鉴权认证
                        .anyRequest().authenticated().and()
                        // 认证失败处理类
                        .exceptionHandling()
                        .authenticationEntryPoint(unauthorizedHandler) // 认证失败处理
                        .accessDeniedHandler(accessDeniedHandler)// 无权访问处理
                        .and()
                        .headers().frameOptions().disable();
                httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
                // 添加JWT filter,addFilterAt添加的是同等级序号
                httpSecurity.addFilterBefore(wxAppletAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
                httpSecurity.addFilterBefore(userPasswordAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
                httpSecurity.addFilterAfter(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        }

        /**
         * 身份认证接口
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception
        {
                // 微信登录方式
                auth.authenticationProvider(wxAppletAuthenticationProvider);
                // 前台用户密码 登录方式
                auth.authenticationProvider(mobileCodeAuthenticationProvider);
                // 后台用户名密码登录方式
                auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());

        }


        //这个必须重写，才能使用AuthenticationManager，在成员变量注入进来，再注入过滤器中


        @Override
        @Bean
        protected AuthenticationManager authenticationManager() throws Exception {
                return super.authenticationManager();
        }

        /**
         * 微信登录filter 配置
         * @return
         * @throws Exception
         */
        @Bean
        public WxAppletAuthenticationFilter wxAppletAuthenticationFilter() throws Exception {
                WxAppletAuthenticationFilter filter = new WxAppletAuthenticationFilter();
                filter.setAuthenticationManager(authenticationManager());
                filter.setAuthenticationSuccessHandler(wxAppletAuthenticationnSuccessHandler);
                filter.setAuthenticationFailureHandler(wxAuthenticationFailureHandler);
                return filter;
        }

        @Bean
        public MobileCodeAuthenticationFilter userPasswordAuthenticationProcessingFilter() throws  Exception{
                MobileCodeAuthenticationFilter filter = new MobileCodeAuthenticationFilter();
                filter.setAuthenticationManager(authenticationManager());
                filter.setAuthenticationSuccessHandler(passwordAuthenticationnSuccessHandler);
                filter.setAuthenticationFailureHandler(wxAuthenticationFailureHandler);
                return filter;
        }


        //注入密码编码器
        @Bean
        public PasswordEncoder passwordEncoder() {
                //return NoOpPasswordEncoder.getInstance(); // 不加密
                return new BCryptPasswordEncoder();// BCryptPasswordEncoder加密
        }




        /**
         * 强散列哈希加密实现
         */
        @Bean
        public BCryptPasswordEncoder bCryptPasswordEncoder()
        {
            return new BCryptPasswordEncoder();
        }


        @Override
        public void configure(WebSecurity web) throws Exception {
                //解决静态资源被拦截的问题
                web.ignoring().antMatchers("/static/**","/favicon.ico","/error","/index.html");
        }


        public static void main(String[] args) {
                BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
                final String passHash = encoder.encode("admin123");
                //每次的随机盐已经保存在了passHash 中，所以每次同一个数据加出来的密也不会一样，但是匹配的时候会匹配上
                System.out.println(passHash);
                System.out.println(encoder.matches("admin123","$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2"));

        }
    }
